// 实例拦截在创建实例传参时设置
// 全局拦截在构造函数REDRequest里面设置
// 单个请求拦截在调用创建好的实例.request({}) 里面传.
import axios from 'axios';
import type { AxiosInstance } from 'axios';
import type { REDRequestInterceptors, REDRequestConfig } from './type';
import { ElLoading } from 'element-plus';
import { ILoadingInstance } from 'element-plus/lib/components/loading/src/loading.type';

// 为axios增加可以传入拦截器的功能
class REDRequest {
  instance: AxiosInstance; // 需要传递的原生参数
  interceptors?: REDRequestInterceptors; // 传递实例拦截器的参数
  loadingInstance?: ILoadingInstance; // 无需传递, loading界面.
  showLoading?: boolean; // 默认可以不传,但是一定要有这个. 在下面我会默认给你赋值. 再做判断.

  constructor(config: REDRequestConfig) {
    this.instance = axios.create(config);
    // 传参都在一个对象里面 config: REDRequestConfig. 对象里面有interceptors
    this.interceptors = config.interceptors;
    // this.showLoading赋值不了. 只能使用(config as REDRequestConfig).showLoading判断
    this.showLoading = config.showLoading;

    // 创建实例的拦截器
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestInterceptorCatch
    );

    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseInterceptorCatch
    );

    // 所有实例都有的拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // console.log('所有实例都有的拦截器, 请求发送成功拦截');

        // 添加loading
        // console.log((config as REDRequestConfig).showLoading, 123);
        // console.log('this.showLoading ', this.showLoading);
        // console.log(config);

        if ((config as REDRequestConfig).showLoading) {
          console.log('开启loading');

          this.loadingInstance = ElLoading.service({
            lock: true,
            text: '正在请求数据...',
            fullscreen: true,
            background: 'rgba(0, 0, 0, 0.5)'
          });
        }

        return config;
      },
      (err) => {
        // console.log('所有实例都有的拦截器, 请求发送失败拦截');
        return err;
      }
    );
    this.instance.interceptors.response.use(
      (res) => {
        // console.log('所有实例都有的拦截器, 响应成功拦截');

        // 响应里面config都没有

        // 移除loading
        // 创建了就会有, 没有就是undefined
        // console.log(this.loadingInstance);

        if (this.loadingInstance) {
          console.log('关闭loading');
          this.loadingInstance?.close();
        }

        const data: any = res.data;
        // 判断失败信息
        if (data.returnCode === '-1001') {
          console.log('请求失败~, 错误信息');
        } else {
          return data;
        }
        return res;
      },
      (err) => {
        // console.log('所有实例都有的拦截器, 响应失败拦截');
        // 判断不同的HttpErrorCode显示不同的错误信息
        if (err.response.status === 404) {
          console.log('404错误');
        }
        this.loadingInstance?.close();
        return err;
      }
    );
  }

  // new REDRequest({...})后.request({})发送请求返回数据.
  // config: REDRequestConfig包括了原生的axios参数.
  request<T = any>(config: REDRequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      // 判断是否传入请求拦截器,对config的处理(单个请求的拦截器)
      if (config.interceptors?.requestInterceptor) {
        // 先让单个请求拦截器执行完操作, 最后都会返回config
        config = config.interceptors?.requestInterceptor(config);
      }

      // 这个是这个请求的返回处理
      // 这里是真正发送请求的地方
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 判断是否传入响应拦截器(单个响应的拦截器)
          if (config.interceptors?.responseInterceptor) {
            // 执行这个拦截器的操作
            res = config.interceptors?.responseInterceptor(res);
          }
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

  get<T = any>(config: REDRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'GET' });
  }
  post<T = any>(config: REDRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'POST' });
  }
  delete<T = any>(config: REDRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'DELETE' });
  }
  patch<T = any>(config: REDRequestConfig): Promise<T> {
    return this.request<T>({ ...config, method: 'PATCH' });
  }
}

export default REDRequest;
